package com.google.code.virtualhockey.vhx.tools.dataimport

import com.google.code.virtualhockey.vhx.util.CSVParser
import com.google.code.virtualhockey.vhx.server.model._
import com.google.appengine.tools.remoteapi.{RemoteApiInstaller, RemoteApiOptions}
import com.google.code.virtualhockey.vhx.sds.{STEntity, Sds}
import java.io.File

/**
 * Imports data that is available in CVS format into the GAE datastore.
 */
object DataImport extends App {

  private val PROD_SERVER = ( "vhx-hockey.appspot.com", 443 )
  private val DEV_SERVER  = ( "localhost", 8888 )

  // execute the program
  //
  run()
  
  /**
   * Imports the given CSV file into either the production or development datastore.
   *
   * @param file the full path of the file to be imported
   * @param entityType the type of the entity to be imported
   * @param isProd true if the file should be imported to production
   * @param userName the name of the user which should be used to connect to the datastore
   * @param password the user's password
   */
  private def importFromCsv( file: String, entityType: String, isProd: Boolean, userName: String, password: String ) {
    val server = if ( isProd ) PROD_SERVER._1 else DEV_SERVER._1
    val port = if ( isProd ) PROD_SERVER._2 else DEV_SERVER._2
    val metadata = Schema.getMetaData( entityType )
    val apiOptions = new RemoteApiOptions().server( server, port ).credentials( userName, password )

    Sds.setSchema( Schema )
    new RemoteApiInstaller().install( apiOptions )

    val importedLines: List[List[Any]] = CSVParser.parseFile( file )
    val importedObjects: List[STEntity[_]] = importedLines.filter( !_.isEmpty ).map( line => metadata.fromCsv( line ).asInstanceOf[STEntity[_]] )
    var count = 1

    importedObjects.foreach( obj => { obj.put(); println( "%3d".format( count ) + " - Imported: " + obj.toString ); count += 1; } )
  }

  /**
   * Executes the actual data import.
   */
  private def run() {
    if ( this.args.isEmpty )
      runInteractiveMode()
    else
      runCmdLineMode()

    sys.exit( 0 )
  }

  private def runCmdLineMode() {
    val usage = """Usage: DataImport --targetEnv P|D --type string --user string --password string file"""
    val opts: Map[Symbol,String] = parseCommandLine( this.args )

    val file: String = opts.getOrElse( 'file, { println( usage ); sys.exit( -1 ); } )
    if ( !new File( file ).exists() ) {
      println( "Error: " + file + " is not a valid path." )
      sys.exit( 1 )
    }

    val entityType: String = opts.getOrElse( 'type, { println( usage ); sys.exit( -1 ); } )
    try {
      Schema.getMetaData( entityType )
    } catch {
      case ex: Exception => {
        println( "Error: " + ex.getMessage )
        sys.exit( 2 )
      }
    }

    val targetEnv: String = opts.getOrElse( 'targetEnv, { println( usage ); sys.exit( -1 ); } )
    if ( targetEnv != "P" && targetEnv != "D" ) {
      println( "Error: " + targetEnv + " is not a valid value." )
      sys.exit( 3 )
    }

    val userName: String = opts.getOrElse( 'user, { println( usage ); sys.exit( -1 ); } )

    val password: String = opts.getOrElse( 'password, { println( usage ); sys.exit( -1 ); } )

    importFromCsv( file, entityType, targetEnv == "P", userName, password )
  }

  private def runInteractiveMode() {
    print( "> CSV File to import (full path): " )
    val file = Console.readLine();
    if ( !new File( file ).exists() ) {
      println( "Error: " + file + " is not a valid path." )
      sys.exit( 1 )
    }

    print( "> Type of the entities to be imported: " )
    val entityType = Console.readLine();
    try {
      Schema.getMetaData( entityType )
    } catch {
      case ex: Exception => {
        println( "Error: " + ex.getMessage )
        sys.exit( 2 )
      }
    }

    print( "> GAE user name: " )
    val userName = Console.readLine();

    print( "> GAE password: " )
    val password = Console.readLine();

    print( "> Run on production (P) or dev (D): " )
    val targetEnv = Console.readLine();
    if ( targetEnv != "P" && targetEnv != "D" ) {
      println( "Error: " + targetEnv + " is not a valid value." )
      sys.exit( 3 )
    }

    importFromCsv( file, entityType, targetEnv == "P", userName, password )
  }

  private def parseCommandLine( args: Array[String] ): Map[Symbol,String] = {

    def nextOption( map: Map[Symbol,String], listArgs: List[String] ) : Map[Symbol,String] = {
      def isSwitch( s: String ) = ( s(0) == '-' )

      listArgs match {
        case Nil => map
        case "--user" :: value :: tail => nextOption( map ++ Map( 'user -> value ), tail )
        case "--password" :: value :: tail => nextOption( map ++ Map( 'password -> value ), tail )
        case "--type" :: value :: tail => nextOption( map ++ Map( 'type -> value ), tail )
        case "--targetEnv" :: value :: tail => nextOption( map ++ Map( 'targetEnv -> value ), tail )
        case opt1 :: opt2 :: tail if isSwitch( opt2 ) => nextOption( map ++ Map( 'file -> opt1 ), listArgs.tail )
        case opt :: Nil =>  nextOption( map ++ Map( 'file -> opt ), listArgs.tail )
        case opt :: tail => println( "Unknown option " + opt ); sys.exit( -1 )
      }
    }

    nextOption( Map(), args.toList )
  }
}
